﻿///////////////////////////////////////////////////////////////////////////////
// Copyright 2016 lingzimaike@gmail.com
// GPLv2
// Author: lingzimaike
///////////////////////////////////////////////////////////////////////////////

#ifndef PAPA_APPLICATION_H_
#define PAPA_APPLICATION_H_

#include <string>
#include <vector>
#include <papa/base.h>
#include <papa/util/logger.h>

namespace papa {

	class Application
	{
	public:
		enum ExitCode
		{
			EXIT_OK         = 0,
			EXIT_USAGE      = 64,
			EXIT_CONFIG     = 65,
			EXIT_SOFTWARE   = 70,
		};

		Application(void);
		virtual ~Application(void);

		int Init(int argc, char* argv[]);
		int Run(int argc, char* argv[]);

		void HookSignal(int signal);
		void UnhookSignal(int signal);
		static Application* GetMe(void);

        util::Logger& get_logger(void) { return logger_; }

	protected:
		class Option
		{
		public:
			Option(const char* long_name, const char* short_name, const char* help_string);
			~Option(void);

			bool MatchLongName(const ::std::string& name);
			bool MatchShortName(const ::std::string& name);

			const ::std::string& get_long_name(void) const { return long_name_; }
			const ::std::string& get_short_name(void) const { return short_name_; }
			void is_required(bool required) { required_ = required; }
			bool is_required(void) const { return required_; }
			void has_argument(bool has) { has_argument_ = has; }
			bool has_argument(void) const { return has_argument_; }
			void set_value(const ::std::string& value) { value_ = value; }
			const ::std::string& get_value(void) const { return value_; }
			void is_valid(bool valid) { valid_ = valid; }
			bool is_valid(void) const { return valid_; }

		private:
			::std::string long_name_;
			::std::string short_name_;
			::std::string help_string_;
			bool required_;
			bool has_argument_;
			::std::string value_;
			bool valid_;
		};

		class OptionSet
		{
		public:
			OptionSet(void);
			~OptionSet(void);

			void AddOption(Option& option);
			void Process(::std::vector<std::string>& arguments);

			const ::std::vector<Option>& get_options(void) const { return options_; }
		private:
			::std::vector<Option> options_;

			PAPA_DISALLOW_COPY_AND_ASSIGN(OptionSet)
		};

        virtual int Initialize(void);
        virtual void Uninitialize(void);

		virtual void DefineOptions(OptionSet& option_set);
		virtual void HandleOption(const char* name, const char* value);
		virtual void PrintUsage(OptionSet& option_set);
		virtual int Main(const ::std::vector<std::string>& unhandled_arguments) = 0;
		virtual void HandleStdException(::std::exception& e);
		virtual void HandleSystemException(void);
		virtual void HandleSignal(int signal);

	private:
		bool ProcessOptions(void);
		static void SignalHandler(int signal);

		::std::vector<std::string> unhandled_arguments_;
		OptionSet option_set_;

        util::Logger logger_;

		static Application* sapp_;

		PAPA_DISALLOW_COPY_AND_ASSIGN(Application)
	};

}  // namespace papa

#endif // PAPA_APPLICATION_H_
